/*	This software is under  GNU GENERAL PUBLIC LICENSE Version 2, June 1991
*	for more information read COPYING.TXT
*	Dimitri Sean Ribeiro - 5118549
*/

//include section
#include "shell.h"

/******************************************************************************************/

/*
 *	list the backgroung process in a file.
 *
 */

void write_bg_file(jobs *job, int job_no) {

	int i = 0;
	FILE *fp;

	fp = fopen(ARQUIVO, "w");
	
	//write in file
	for(i = 0; i < job_no; i++) {
		fprintf(fp,"%d_%d_%s_\n", i, job[i].job_id, job[i].job_name);
	}
	
	//write the symbol $$ at the end of file
	fprintf(fp, "$$\n");
	
	if(fclose(fp)) {
		fatal();
		exit(EXIT_FAILURE);
	}

}

/******************************************************************************************/

/*
 *	reads the file that store the background process.
 *
 */
 
jobs * read_bg_file() {

	jobs *job;

	job = (jobs*) malloc (1000*sizeof(jobs));

	FILE *fp;
	char *buffer, *argz, **information;
	int i;
	size_t argz_length, buffer_size, read_bytes;
	
	information = (char **) malloc (4*sizeof(char *));
	
	for(i = 0; i < 4; i++) {
		information[i] = (char *) malloc (20*sizeof(char));
	}
	
	fp = fopen(ARQUIVO, "r");
	
	while(1) {
		buffer = (char *) malloc (80*sizeof(char));
		
		//reads the file's first line and removes \n
        read_bytes = getline(&buffer, &buffer_size, fp);
        buffer[read_bytes-1]= '\0';
        
        //last line
		if(strcmp(buffer, "$$") == 0) {
        	free(buffer);
        	break;
        }
        else {
        	//get the information spliting the values by '_'
			if (argz_create_sep (buffer, '_', &argz, &argz_length)) {
				fprintf(stderr,"can't create vector jobs.\n");
				exit (EXIT_FAILURE);
			}
			
			//copy the values to the vector information
			argz_extract (argz, argz_length, information);
			
			//store the informations
			job[atoi(information[0])].job_id = atoi(information[1]);
			job[atoi(information[0])].job_name = information[2];
			
			free(buffer);
		}
	}

	free(information);

	if(fclose(fp)) {
		fatal();
		exit(EXIT_FAILURE);
	}
	
	//retorns the structure that has the process
	return job;
}

/******************************************************************************************/

/*
 *	add a process at the background list.
 *
 */
 
void add_process(command_line_t comando, int pid) {

	jobs *job;
	int job_no = 0;
	
	job = (jobs*) malloc (1000*sizeof(jobs));
	job[job_no].job_name = NULL;
	
	//reads the file that store the background process
	job = read_bg_file();

	//count the numer of actives process
	while(job[job_no].job_id != 0) {
		job_no++;
	}
	
	//update the pid and the name of process
	job[job_no].job_id = pid;
	job[job_no].job_name = comando.argv[comando.argc - 1];
	
	printf("[%d] %d\t%s\n", job_no, job[job_no].job_id, job[job_no].job_name);

	job_no++;

	//update the file
	write_bg_file(job, job_no);
}

/*
 *	removes a process pid from background list
 *
 */
 
void remove_processo(int pid) {

	jobs *job;
	int job_no = 0;
	
	job = (jobs*) malloc (1000*sizeof(jobs));

	int i = 0;
	
	//reads the file that store the background process
	job = read_bg_file();

	//count the numer of actives process
	while(job[job_no].job_id != 0) {
		job_no++;
	}
	
	//search the process pid and remove it replacing the pid = -1 anf name=''
	for(i = 0; i < job_no; i++) {
		if(job[i].job_id == pid) {
			job[i].job_id = -1;
			job[i].job_name = " ";
		}
	}
	
	//update the file
	write_bg_file(job, job_no);

}

/*
 *	list background process.
 *
 */
 
void list_processos() {

	jobs *job;
	int job_no = 0;
	
	job = (jobs*) malloc (1000*sizeof(jobs));

	int i = 0;

	//reads the file that store the background process
	job = read_bg_file();

	//count the numer of actives process
	while(job[job_no].job_id != 0) {
		job_no++;
	}

	//print the active process
	for(i = 0; i < job_no; i++) {
		if(job[i].job_id != -1) {
			printf("[%d] %d\t%s\n", i, job[i].job_id, job[i].job_name);
		}
	}
	
}

/******************************************************************************************/

/*
 *	Get the signals sent to the process:
 *
 *	get_signal_SIGINT:  interruption signal
 *	get_signal_SIGCONT:  continue signal
 *	get_signal_SIGTSTP:  stop signal
 *
 */
 
//Ctrl+c
void get_signal_SIGINT(int signum)
{
   printf("Interrupted [%d].\n",getpid());
   fflush(stdout);
   signal(SIGINT, get_signal_SIGINT);
}

void get_signal_SIGCONT(int signum)
{
   fflush(stdout);
   signal(SIGCONT,get_signal_SIGCONT);
}

//Ctrl+z
void get_signal_SIGTSTP(int signum)
{
   printf("Stopped [%d].\n",getpid());
   fflush(stdout);
   signal(SIGTSTP,get_signal_SIGTSTP);
}

/******************************************************************************************/

/*
*	EN:
*	this is the main function. It print the prompt, read the input command,
*	call parser and verify the command. Internal commands: exit, fg, bg,
*	jobs, echo, pwd, cd, help, ajuda.
*
*	PT:
 *	Esta é função principal. Ela imprime o PROMPT, lê o comando de entrada, 
 *	chama o parser e verifica se é um comando interno ou não. Os comandos
 *	internos são:
 *
 *	exit:		sai do soiishell
 *	fg:		traz o processo que estava em background para foreground
 *	bg:		executa um processo (estava parado) em background		
 *	jobs:		lista os processos em background
 *	echo:		imprime na tela os argumenos separados por um espaço
 *	pwd:		mostra o diretório corrente
 *	cd:		muda o diretório corrente
 *	help:		mostra uma ajuda sobre as funcionalidades do soiishell
 *	ajuda:	o mesmo que o help
 *
 */
 
int main(int c, char **argv)
{
	char *buf = NULL; //store the input command
	command_line_t comando;	//store the command splited by the parser
	size_t buf_size, read_bytes; //buffers size
	int i = 0;
	
	
	FILE *fp;	//background process file
	fp = fopen(ARQUIVO,"w");
	fprintf(fp, "$$\n");	//add $$ at the end of file
	fclose(fp);

	signal(SIGINT, get_signal_SIGINT);
	signal(SIGCONT,get_signal_SIGCONT);
	signal(SIGTSTP,get_signal_SIGTSTP);

	system("clear");

	printf(": soiishell :\n"); //print a welcome message
	
	//main shell
	while(1) {
		fflush(stdin);
		fflush(stdout);
		buf = NULL;
		buf_size = 0;
    
		//print PROMPT and read the input command 
		printf("%s@%s: ", PROMPT, get_current_dir_name());
		read_bytes = getline(&buf, &buf_size, stdin);
    
		if (read_bytes > 1) {
			buf[read_bytes-1]= '\0';   //remove \n
			parse_command_line(buf, &comando); //call parser
		}
		else continue;
		
		//exit command
		if (strcmp (comando.argv[0], "exit") == 0) {
    	    remove(ARQUIVO);
			system("clear");
			exit(EXIT_SUCCESS);
		}
		//fg command
		else if (strcmp (comando.argv[0], "fg") == 0) {
			if (kill(atoi(comando.argv[1]), SIGCONT) == 0) {
				remove_processo(atoi(comando.argv[1]));
				waitpid(atoi(comando.argv[1]), 0, 0);
			}
			else {
				printf("Process not found\n");
			}
		}
		//bg command
		else if (strcmp (comando.argv[0], "bg") == 0) {
			kill(atoi(comando.argv[1]), SIGTSTP);
		}
		//jobs command
		else if(strcmp(comando.argv[0],"jobs") == 0) {
			list_processos();
		}
		//echo command
		else if(strcmp(comando.argv[0],"echo") == 0) {
			for(i = 1; i < comando.argc; i++) {
				printf("%s ", comando.argv[i]);
			}
			printf("\n");
		}
		//pwd command
		else if(strcmp(comando.argv[0],"pwd") == 0) {
			printf("%s\n", get_current_dir_name());
		}
		//cd command
		else if (strcmp (comando.argv[0], "cd") == 0) {
			if(chdir(comando.argv[1])) {
				switch(errno) {
					case(ENOENT):
						fprintf(stderr,"Directory not found\n");
						break;
					case(EACCES):
						fprintf(stderr,"denied permission\n");
						break;
					case(ENOTDIR):
						fprintf(stderr,"not a directory\n");
						break;
					default:
						fprintf(stderr,"Erro\n");
				}
			}
		}
		//help command
		else if (strcmp (comando.argv[0], "help") == 0 || strcmp (comando.argv[0], "ajuda") == 0) {
			printf("soiishell Help\n\n");
			printf("Instructions:\n");
			printf(">  - redirect the output to a file (cria/overwrite)\n\tUse: command  > filename\n");
			printf(">> - redirect the output to a file (append)\n\tUse: command  >> filename\n");
			printf("2> - redirect the errors output to a file\n\tUse: command  2> filename\n");
			printf("&> - redirect the errors output and the normal output to a file\n\tUse: command &> filename\n");
			printf("<  - redirect the input to a file\n\tUse: command < filename\n");
			printf("|  - create a pipe between two process\tUse: command | command\n");
			printf("&  - execute a command at background\n\tUse: command &\n");
			printf("fg - put a process in foreground.\n\tUse: fg [id do processo]\n");
			printf("bg - put a process in background.\n\tUse: bg [id do processo]\n");
			printf("cd - change the current directory\n\tUse: cd [diretorio]\n");
			printf("exit  - exit the soiishell\n");
		}
		//is not a internal command
		else {
			select_command(comando);
		}
		fflush(stdin);
		fflush(stdout);

		free_com(&comando, 0);
		free(buf);
		buf = NULL;
		buf_size = 0;
  }
 
  return(EXIT_SUCCESS);
}
/******************************************************************************************/
